using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens;
using PowerManage.API.Auth;
using PowerManage.API.Captcha;
using PowerManage.API.RateLimiter;
using PowerManage.Entity;
using PowerManage.Entity.Dtos.Auths;

namespace PowerManage.API.Controllers;

/// <summary>
/// 授权
/// </summary>
[Route("api/[controller]")]
public class AuthController : BaseController
{
    private readonly SignInManager<EleUser> _signInManager;
    private readonly IConfiguration _configuration;
    private readonly IMemoryCache _memoryCache; //内存缓存
    private readonly UserManager<EleUser> _userManager;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthController(SignInManager<EleUser> signInManager, IConfiguration configuration, IMemoryCache memoryCache,
        UserManager<EleUser> userManager, IHttpContextAccessor httpContextAccessor)
    {
        _signInManager = signInManager;
        _configuration = configuration;
        _memoryCache = memoryCache;
        _userManager = userManager;
        _httpContextAccessor = httpContextAccessor;
    }

    /// <summary>
    /// 获取验证码接口
    /// </summary>
    /// <returns></returns>
    [HttpGet("code")]
    [AllowAnonymous]
    public IActionResult GetCaptcha()
    {
        //生成验证码
        var code = CaptchaGenerator.CreateValidateCode(4);
        var buff = CaptchaGenerator.GenerateCode(code, 90, 30);
        //验证码唯一识别key
        var codeKey = Guid.NewGuid().ToString();
        //将验证码存入内存缓存
        _memoryCache.Set(codeKey, code, DateTimeOffset.Now.AddMinutes(5));
        return Ok(new
        {
            codeKey = codeKey,
            image = "data:image/png;base64," + Convert.ToBase64String(buff)
        });
    }

    /// <summary>
    /// 登录
    /// </summary>
    /// <param name="authLoginDto"></param>
    /// <returns></returns>
    [HttpPost("login")]
    [AllowAnonymous]
    [EnableRateLimiting(RateLimiterPolicyName.LoginPolicy)]
    public async Task<IActionResult> Login([FromBody] AuthLoginDto authLoginDto)
    {
        //校验验证码
        if (string.IsNullOrEmpty(authLoginDto.CodeKey))
        {
            return StatusCode(StatusCodes.Status501NotImplemented, "验证码错误");
        }
        else
        {
            var code = _memoryCache.Get(authLoginDto.CodeKey);
            if (code == null || !code.ToString().Equals(authLoginDto.Code, StringComparison.OrdinalIgnoreCase))
            {
                return StatusCode(StatusCodes.Status501NotImplemented, "验证码错误！");
            }
        }

        //根据用户名、密码校验
        //bool isPersistent：是否持久化登录状态 表示用户选择了“记住我”（Remember Me）选项
        //bool lockoutOnFailure：表示是否在登录失败时触发锁定机制
        var result = _signInManager.PasswordSignInAsync(authLoginDto.UserName, authLoginDto.Password, false, false);
        if (result.Result.Succeeded)
        {
            // //定义jwt的payload部分 创建一个包含用户声明的列表 这种做法常用于身份验证和授权过程中
            // var claims = new[]
            // {
            //     new Claim(ClaimTypes.Name, authLoginDto.UserName)
            // };
            var token = GenerateToken(authLoginDto.UserName);
            return Ok(token);
        }
        else
        {
            var responseResult = new ResponseResultDto().SetError("用户名或密码错误");
            return BadRequest(responseResult);
        }
    }

    /// <summary>
    /// 通过用户名生成token
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    private AuthLoginResponseDto GenerateToken(string userName)
    {
        var result = new AuthLoginResponseDto();
        //定义jwt的payload部分 创建一个包含用户声明的列表 这种做法常用于身份验证和授权过程中
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, userName)
        };
        //生成token
        var jwtBearer = _configuration.GetSection(AppSettings.Authentication).GetSection(AppSettings.JwtBearer);
        var securityKey =
            new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(jwtBearer[AppSettings.SecurityKey] ?? string.Empty)); //创建对称密钥
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); //创建签名凭证
        //创建JWT安全令牌
        var jwtSecurityToken = new JwtSecurityToken(
            issuer: jwtBearer[AppSettings.Issuer], //设置JWT令牌的发行者 发行者是指生成和签发JWT令牌的服务或实体。通常是一个URL或标识符，表示该令牌是由哪个服务签发的
            audience: jwtBearer[AppSettings.Audience], //设置JWT令牌的受众 受众是指 JWT 令牌的目标接收方，通常是一个URL或标识符，表示该令牌是为哪个服务或实体生成的
            claims: claims, //设置JWT令牌中的声明。claims通常是一个Claim[]数组 如用户、角色等
            notBefore: DateTime.Now, //设置JWT令牌的生效时间 令牌立即生效
            expires: DateTime.Now.AddDays(1), //设置JWT令牌的过期时间 令牌将在一天后过期
            // expires: DateTime.Now.AddMinutes(1), //设置JWT令牌的过期时间 令牌将在一天后过期 测试
            signingCredentials: credentials //设置JWT令牌的签名凭证 签名凭证用于对JWT令牌进行签名，确保令牌的完整性和防篡改性
        );
        result.Token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

        //定义JWT的Payload部分
        var claimsRefreshToken = new[]
        {
            new Claim(ClaimTypes.Name, userName),
            new Claim(ClaimTypes.Role, AppSettings.RefreshTokenRole),
        };
        //生成RefreshToken
        var securityRefreshToken = new JwtSecurityToken(
            issuer: jwtBearer[AppSettings.Issuer], //设置JWT令牌的发行者 发行者是指生成和签发JWT令牌的服务或实体。通常是一个URL或标识符，表示该令牌是由哪个服务签发的
            audience: jwtBearer[AppSettings.Audience], //设置JWT令牌的受众 受众是指 JWT 令牌的目标接收方，通常是一个URL或标识符，表示该令牌是为哪个服务或实体生成的
            claims: claimsRefreshToken, //设置JWT令牌中的声明。claims通常是一个Claim[]数组 如用户、角色等
            notBefore: DateTime.Now, //设置JWT令牌的生效时间 令牌立即生效
            expires: DateTime.Now.AddDays(2), //设置JWT令牌的过期时间 令牌将在一天后过期
            // expires: DateTime.Now.AddMinutes(2), //设置JWT令牌的过期时间 令牌将在一天后过期 测试
            signingCredentials: credentials //设置JWT令牌的签名凭证 签名凭证用于对JWT令牌进行签名，确保令牌的完整性和防篡改性
        );
        result.RefreshToken = new JwtSecurityTokenHandler().WriteToken(securityRefreshToken);
        return result;
    }

    [HttpDelete("logout")]
    public async Task<IActionResult> Logout()
    {
        return Ok();
    }

    /**
     * 刷新令牌是不允许访问业务API的，只允许访问刷新令牌接口
     *
     * 刷新令牌有严格的使用限制：
        1.只能用于获取新的访问令牌：不能用于直接访问业务 API
        2.较短有效期：通常比访问令牌有效期长，但仍然有限
        3.单次使用：通常使用后即失效

        假设我们有一个系统，有以下端点：
        /api/login - 获取初始的访问令牌和刷新令牌
        /api/refreshToken - 使用刷新令牌获取新的访问令牌
        /api/products - 业务 API，需要有效的访问令牌
     */
    [HttpPost("refreshToken")]
    [PermissionAuthorize]
    public async Task<IActionResult> RefreshToken()
    {
        var userName = _httpContextAccessor.HttpContext.User.Identity.Name;

        // 检查用户名是否为空，返回401而不是抛出异常
        if (string.IsNullOrEmpty(userName))
        {
            return StatusCode(StatusCodes.Status401Unauthorized);
        }

        //Task.Run 确实是异步的，但它是一种"强制异步"的方式，适用于运行CPU密集型同步代码。增加性能开销
        // 在ASP.NET Core中，IO操作应该直接用 async/await，而不是 Task.Run
        // 如果 GenerateToken 是轻量级同步操作，Task.FromResult 比 Task.Run 更合适
        var token = await Task.Run(() => GenerateToken(userName));
        return Ok(token);
    }
}